<template>
  <div class="markdown-editor-container">
    <!-- 导航栏 -->
    <div class="editor-navbar">
      <div class="goBack">
        <router-link to="/community/article" replace>
          <span>
            <i class="iconfont icon-xiala"></i>
          </span>
          <b style="font-weight: normal; margin-left: 6px">文章管理</b>
        </router-link>
      </div>
      <!-- 标题框 【无标题】-->
      <div class="title">
        <input
          type="text"
          name=""
          id=""
          v-model="ruleForm.title"
          class="w-100"
          :placeholder="
            showLength ? '【无标题】' : '请输入文章标题（5~100个字）'
          "
        />
        <span class="length" v-show="showLength"
          >{{ ruleForm.title.length }}/100</span
        >
        <span class="unlength" v-show="!showLength"
          >还需要输入{{ 5 - ruleForm.title.length }}个字</span
        >
      </div>
      <!-- save|草稿 -->
      <div class="saveBtn">
        <button class="draft">保存草稿</button>
        <button class="save" @click="dialogFormVisible = true">发布文章</button>
      </div>
      <!-- 用户头像信息 -->
      <div class="userInfo">
        <div class="pic-author">
          <img
            v-lazy="userInfo.profile"
            class="img-fluid circle"
            alt="用户头像"
          />
        </div>
      </div>
    </div>
    <!-- 文章描述和上传文章封面 -->
    <el-dialog title="完善文章信息" :visible.sync="dialogFormVisible">
      <!-- 内层选择标签区域 -->
      <el-dialog
        width="40%"
        title="分配相关标签"
        close="inner_dialog"
        :visible.sync="innerVisible"
        append-to-body
      >
        <el-collapse accordion v-model="activeNames">
          <el-collapse-item
            :title="item.title"
            :name="item.id + 1 + ''"
            v-for="item in menu_content"
            :key="item.id"
          >
            <el-checkbox-group v-model="tagsList[item.id].content">
              <el-checkbox
                :label="tag.id"
                v-for="tag in item.content"
                :key="tag.id"
                >{{ tag.name }}</el-checkbox
              >
            </el-checkbox-group>
          </el-collapse-item>
        </el-collapse>
      </el-dialog>
      <!-- 文章描述信息填写 -->
      <el-form
        style="width: 94%; margin: 0 auto"
        :model="ruleForm"
        :rules="rules"
        ref="rulesFrom"
      >
        <!-- 文章标题 -->
        <el-form-item label="文章标题" prop="title" label-width="90px">
          <el-input v-model="ruleForm.title"></el-input>
        </el-form-item>
        <!-- 文章描述信息 -->
        <el-form-item label="描述信息" label-width="90px" prop="description">
          <el-input
            type="textarea"
            v-model="ruleForm.description"
            placeholder="说明文章的大致内容..."
          ></el-input>
        </el-form-item>
        <!-- 上传封面图片 -->
        <!-- prop="pic_bg" -->
        <el-form-item
          label="封面图片"
          label-width="90px"
          ref="pic_elem"
          prop="pic_bg"
        >
          <!-- 这里收集数据不能使用v-model
            因为这个不是表单元素
            action上传图片地址的路径，这里地址一定注意fileUpload注意U大写
           -->
          <el-upload
            v-loading="loadingAnimation.pic_bg"
            class="avatar-uploader"
            :show-file-list="false"
            action=""
            ref="upload"
            :before-upload="beforeAvatarUpload"
          >
            <img
              v-if="ruleForm.pic_bg"
              :src="ruleForm.pic_bg"
              class="avatar img-fluid rounded h-100"
            />
            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
            <div slot="tip" class="el-upload__tip">
              上传图片大小不能超过 1 MB
            </div>
          </el-upload>
        </el-form-item>
        <!-- 图标选择区域 -->
        <el-form-item
          label="相关标签"
          label-width="90px"
          style="display: flex"
          prop="tagLength"
          ref="tagBox"
        >
          <el-button type="info" class="fenpei" plain @click="dispatch"
            >分配标签</el-button
          >
          <span style="margin-left: 20px">已选择 {{ countTags }}/6 个</span>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="markPush('rulesFrom', '已发布')"
          >发 布</el-button
        >
      </div>
    </el-dialog>
    <!-- 编辑器 -->
    <mavon-editor
      v-model="value"
      :boxShadow="true"
      :navigation="false"
      :autofocus="true"
      :toolbars="toolbars"
      :stripIgnoreTag="false"
      codeStyle="androidstudio"
      fontSize="16px"
      ref="md"
      @imgAdd="$imgAdd"
    >
    </mavon-editor>
  </div>
</template>

<script>
import { push } from "@/api/mark";
import axios from "axios";
import dayjs from "dayjs";
import { mapState, mapActions } from "vuex";
import { uuid } from "@/utils/uuid";
import { userAugment } from "@/api/users.js";

export default {
  name: "MarkdownEditor",
  data() {
    // 文章封面图片校验
    let pic_bg_validator = (rule, value, callback) => {
      if (value.indexOf(".") === -1) {
        return callback(new Error("请上传文章封面图片"));
      }
      callback();
    };
    // 分配标签验证
    let tagLength_validator = (rule, value, callback) => {
      /**
       * 验证原理
       * 1. 通过全局状态tagList的content数组来确定是否，选择了至少一个id标签
       * 2. 使用一个计算函数，只要tagList数据内容发生改变，立即重新计算是否选择了一个标签
       *  for循环，返回1，表明已选择标签
       *  返回0，没有一个选择，抛错至少选择一个
       */
      if (this.countTags < 1) {
        return callback(new Error("至少选择一个标签🖐"));
      }
      if (this.countTags > 6) {
        return callback(new Error("最多选择六个标签，请不要搞事😅"));
      }
      this.$refs.tagBox.clearValidate();
      callback();
    };
    return {
      // 控制显示加载动画
      loadingAnimation: {
        pic_bg: false, // 封面图片上传加载动画
      },
      // 控制能否上传封面图片，如果图片大于1mb则无法上传 true不能上传，false能上传
      uploadPic_bg_flag: false,
      // 控制显示添加文章描述和封面图片
      dialogFormVisible: false,
      // 控制选择标签的内层
      innerVisible: false,
      // 默认前端折叠框打开
      activeNames: "1",
      id: 15,
      // 文章内容
      value:
        "## 1. 什么是数据库\n\n数据库（database）是用来 `组织、存储` 和 `管理` 数据的仓库\n\n当今世界是一个充满着数据的 `互联网世界，充斥着大量的数据。数据的来源` 有很多，比如出行记录、消费记录，浏览的网页、发送的消息等等。除了文本类型的数据，图像、音乐、声音都是数据\n\n为了方便管理互联网世界中的数据，就有了 `数据库管理系统` 的概念（简称：数据库），用户可以对数据库中的数据进行**新增、查询、更新、删除**等操作。\n\n\n\n## 2. 常见数据库及分类\n\n市面上的数据库有很多种，最常见的数据库有如下几个：\n\n1. `MySQL` 数据库（目前 `使用最广泛、流行度最高` 的开源免费数据库 Community免费版 + Enterprise付费版）\n2. Oracle数据库（收费）\n3. SQL Server数据库（收费）\n4. mongodb 数据库（Community + Enterprise）\n\n\n\n其中，MySQL、Oracle、SQL Server 属于`传统型数据库`（又叫做：**关系型数据库** 或 **SQL数据库**），这三者的\n设计理念相同，用法比较类似\n\n而 Mongodb 属于`新型数据库`（又叫做：**非关系型数据库** 或 **NoSQL数据库**），它在一定程度上弥补了传统型\n数据库的缺陷\n\n",
      // 作者id
      author: "1",
      // 校验图片路径和描述信息
      ruleForm: {
        // 文章标题
        title: "mysql保存emoji报java.sql.SQLException",
        description:
          "最近在做即时聊天功能，需要支持 emoji 表情，测试直接发送时后台 mysql 数据库会报类似 java.sql.SQLException: Incorrect string value😅",
        // 品牌对象
        pic_bg: "",
        tagLength: "",
      },
      rules: {
        title: [
          { required: true, message: "请输入文章标题", trigger: "blur" },
          {
            min: 5,
            max: 100,
            message: "需要输入5-100个描述文字",
            trigger: "blur",
          },
        ],
        description: [
          { required: true, message: "请输入文章描述信息", trigger: "blur" },
          { min: 10, message: "最少10个描述文字", trigger: "blur" },
        ],
        pic_bg: [
          {
            validator: pic_bg_validator,
            trigger: "blur",
            required: true,
          },
        ],
        tagLength: [
          {
            validator: tagLength_validator,
            trigger: "blur",
            required: true,
          },
        ],
      },
      // GitHub 配置参数
      github: {
        // https://api.github.com/repos/用户名/仓库名/contents/文件路径
        Folder: "img", // 存放文件夹
        url: "https://api.github.com/repos", // api接口地址
        token: "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", // github 授权令牌
        owner: "ChaiMayor", // github 用户名
        repo: "markImages", // 图库名
      },
      // 发布文章参数
      publish: {
        title: "",
        description: "",
        pic_bg: "",
        content: "",
        tags: [],
        createTime: "",
        updateTime: "",
        author: "",
        status: "",
      },
      toolbars: {
        bold: true, // 粗体
        italic: true, // 斜体
        header: true, // 标题
        underline: true, // 下划线
        strikethrough: true, // 中划线
        mark: true, // 标记
        superscript: true, // 上角标
        subscript: true, // 下角标
        quote: true, // 引用
        ol: true, // 有序列表
        ul: true, // 无序列表
        link: true, // 链接
        imagelink: true, // 图片链接
        code: true, // code
        table: true, // 表格
        fullscreen: true, // 全屏编辑
        readmodel: true, // 沉浸式阅读
        htmlcode: false, // 展示html源码
        help: true, // 帮助
        /* 1.3.5 */
        undo: true, // 上一步
        redo: true, // 下一步
        trash: true, // 清空
        save: false, // 保存（触发events中的save事件）
        /* 1.4.2 */
        navigation: true, // 导航目录
        /* 2.1.8 */
        alignleft: true, // 左对齐
        aligncenter: true, // 居中
        alignright: true, // 右对齐
        /* 2.2.1 */
        subfield: true, // 单双栏模式
        preview: true, // 预览
      },
    };
  },
  computed: {
    ...mapState("tags", ["menu_content", "tagsList"]),
    ...mapState("user", ["userInfo"]),
    // 计算选中的标签个数，用于验证是否选择了至少一个标签
    countTags() {
      let num = 0;
      for (let i = 0; i < this.tagsList.length; i++) {
        num += +this.tagsList[i].content.length;
      }
      return num;
    },
    // 计算显示字数
    showLength() {
      return this.ruleForm.title.length >= 5 ? true : false;
    },
  },
  methods: {
    ...mapActions("tags", ["resetList"]),
    ...mapActions("user", { updateUserInfo: "updateUserInfo" }),
    // 发起文章收藏浏览评论相关请求
    async toolRequest(flag, article_id) {
      const reqArr = ["article", "draft"];
      let reqIndex = -1;

      // 确定传入参数
      switch (flag) {
        case "jia":
          reqIndex = 0;
          break;
      }
      let attr = reqArr[reqIndex];

      // 开始更新指定数据
      let newValue = !this.userInfo[attr]
        ? { content: [] }
        : JSON.parse(this.userInfo[attr]);
      let newObj = { content: [] };

      // 增加
      newObj.content = [...new Set([...newValue.content, article_id])];

      await userAugment(
        article_id,
        this.userInfo.user_id,
        `${attr}~${JSON.stringify(newObj)}`
      );
      this[attr] = !this[attr];

      // 更新全局用户信息
      this.updateUserInfo();
    },
    // 分配图标
    dispatch() {
      this.innerVisible = true;
      this.$refs.tagBox.clearValidate();
    },
    // 确定上传新的文章
    markPush(formName, status) {
      // 草稿状态代码
      if (status === "草稿") {
        return;
      }
      // 已发布状态代码
      this.$refs[formName].validate(async (valid) => {
        if (valid) {
          let { title, description, pic_bg } = this.ruleForm;
          let time = +dayjs();
          let article_id = uuid();
          // 整合传递的参数
          let options = {
            article_id,
            title: this.$emoji.utf16toEntities(title),
            description: this.$emoji.utf16toEntities(description),
            pic_bg,
            content: this.$emoji.utf16toEntities(this.value),
            tags: JSON.stringify(this.tagsList),
            createTime: time,
            updateTime: time,
            author_init_id: this.userInfo.id,
            author_id: this.userInfo.user_id,
            author_name: this.userInfo.userName,
            status,
          };
          // 发送到数据库
          try {
            let results = await push(options);
            if (results.message) {
              this.dialogFormVisible = false;
              // 对发布结果做出响应
              this.$message({
                message: results.message,
                type: "success",
                duration: 5 * 1000,
              });
              // 重置标签配置
              this.resetList();
              // 增加作者文章数量
              this.toolRequest("jia", article_id);
              // 跳转回社区页面
              this.$router.replace("/community/article");
            }
          } catch (error) {}
        } else {
          return false;
        }
      });
    },
    // 用户上传头像之前触发的回调
    beforeAvatarUpload(file) {
      // 判断文件大小
      let status = file.size / 1024 / 1024 < 1;
      if (!status) {
        this.uploadPic_bg_flag = true;
        return this.$message({
          message: "图片大小不能超过 1 MB",
          type: "error",
          duration: 5 * 1000,
        });
      }
      // 创建文件对象
      let fileReader = new FileReader();
      // 将文件转换为base64位格式编码
      fileReader.readAsDataURL(file);
      // 转换完毕后调用
      fileReader.onload = () => {
        /* 解析完成结果 */
        let result = fileReader.result;
        this.uploadPicbg(file, result);
      };
    },
    // 文章上传封面图片
    uploadPicbg($file, pic) {
      // 取消默认的上传文件请求，改为手动发送
      this.$refs.upload.abort();

      // 判断是否可以上传封面图片
      if (this.uploadPic_bg) {
        return (this.uploadPic_bg_flag = false);
      }

      this.loadingAnimation.pic_bg = true;

      // 配置路径接口
      const filename = `${+dayjs()}.${$file["name"].split(".")[1]}`;
      const filepath = `${this.github.Folder}/${filename}`;
      // 拼接目标路径
      const target = `${this.github.url}/${this.github.owner}/${this.github.repo}/contents/${filepath}`;

      // 文件的base64编码
      const base64 = pic.substring(pic.indexOf(",") + 1); // 取得逗号后面的数据

      // body 参数
      var predata = {
        message: "commit from images",
        content: base64,
        branch: "main",
      };

      axios({
        url: target,
        method: "PUT",
        data: JSON.stringify(predata),
        headers: {
          "Content-Type": "application/vnd.github+json",
          Authorization: `Bearer ${this.github.token}`,
        },
      }).then((options) => {
        // 第二步.在返回的图片信息里找到 path 路径，替换到文本原位置![...](0) -> ![...](url)
        //       使用cdn加速替换原来的github图库地址
        /**
         * $vm 指为mavonEditor实例，可以通过如下两种方式获取
         * 1. 通过引入对象获取: `import {mavonEditor} from ...` 等方式引入后，`$vm`为`mavonEditor`
         * 2. 通过$refs获取: html声明ref : `<mavon-editor ref=md ></mavon-editor>，`$vm`为 `this.$refs.md`
         */
        // $vm.$img2Url(pos, url);
        this.loadingAnimation.pic_bg = false;
        const url = `https://cdn.jsdelivr.net/gh/${this.github.owner}/${this.github.repo}/${options.data.content.path}`;
        this.ruleForm.pic_bg = url;
        // 取消抛错信息
        this.$refs.pic_elem.clearValidate();
        // 提示文件上传成功
        this.$message({
          message: "图片上传成功",
          type: "success",
          duration: 5 * 1000,
        });
      });
    },
    /**
     * @ pos 当前文档上传的图片个数
     * @ $file 上传的文件信息
     */
    // 编辑器上传图片
    $imgAdd(pos, $file) {
      // 第一步.将图片上传到 github 图库.
      var formdata = new FormData();
      formdata.append("image", $file);
      // 配置路径接口
      const filename = `${+dayjs()}.${$file["_name"].split(".")[1]}`;
      const filepath = `${this.github.Folder}/${filename}`;
      // 拼接目标路径
      const target = `${this.github.url}/${this.github.owner}/${this.github.repo}/contents/${filepath}`;

      // 文件的base64编码
      const base64 = $file["miniurl"].substring(
        $file["miniurl"].indexOf(",") + 1
      ); // 取得逗号后面的数据

      // body 参数
      var predata = {
        message: "commit from images",
        content: base64,
        branch: "main",
      };

      axios({
        url: target,
        method: "PUT",
        data: JSON.stringify(predata),
        headers: {
          "Content-Type": "application/vnd.github+json",
          Authorization: `Bearer ${this.github.token}`,
        },
      }).then((options) => {
        // 第二步.在返回的图片信息里找到 path 路径，替换到文本原位置![...](0) -> ![...](url)
        //       使用cdn加速替换原来的github图库地址
        /**
         * $vm 指为mavonEditor实例，可以通过如下两种方式获取
         * 1. 通过引入对象获取: `import {mavonEditor} from ...` 等方式引入后，`$vm`为`mavonEditor`
         * 2. 通过$refs获取: html声明ref : `<mavon-editor ref=md ></mavon-editor>，`$vm`为 `this.$refs.md`
         */
        // $vm.$img2Url(pos, url);
        const url = `https://cdn.jsdelivr.net/gh/${this.github.owner}/${this.github.repo}/${options.data.content.path}`;
        this.$refs.md.$img2Url(pos, url);
      });
    },
  },
  mounted() {},
};
</script>

<style lang="less" scoped>
/deep/ .el-collapse {
  border: 0;
  padding: 0 40px 20px !important;
  .el-collapse-item__header {
    font-size: 16px;
    height: 41px;
  }
}
/deep/ .avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
/deep/ .avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
/deep/ .avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 252px;
  height: 180px;
  line-height: 178px;
  text-align: center;
}
/deep/ .avatar-uploader {
  display: flex;
  flex-wrap: wrap;
  & > div.el-upload {
    position: relative;
    width: 252px;
    height: 180px;
    overflow: hidden;
  }
  img {
    /* 水平垂直居中 */
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
  }
  .el-upload__tip {
    margin-left: 20px;
    text-align: left;
    font-size: 14px;
  }
}
.avatar {
  // width: 252px;
  height: 180px;
  display: block;
}
.markdown-editor-container {
  display: flex;
  flex-direction: column;
  overflow: none;
  .fenpei {
    .el-form-item__error {
      white-space: nowrap;
    }
  }
  .is-required:nth-of-type(4) {
    .el-form-item__content {
      margin-left: 0 !important;
    }
  }
  // 完善信息
  .el-form-item {
    .el-form-item__label {
      font-size: 16px !important;
    }
  }
  .el-textarea {
    textarea {
      height: 80px;
    }
  }
  // 编辑器
  .markdown-body {
    height: calc(100vh - 60px);
  }
  .editor-navbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 58px;
    background: #f4f4f4;
    .goBack {
      flex: auto;
      max-width: 112px;
      display: flex;
      justify-content: flex-end;
      a span {
        display: inline-block;
        transform: rotate(90deg);
        i {
          font-size: 18px;
          color: #8b8b8b;
        }
      }
      a {
        color: #8b8b8b;
      }
      a:hover {
        color: #8b8b8b;
      }
      & > span {
        font-size: 18px;
        color: #8b8b8b;
        margin-left: 4px;
      }
    }
    .title {
      flex: auto;
      position: relative;
      margin: 0 14px;
      border-radius: 24px;
      font-size: 14px;
      height: 38px !important;
      outline: none;
      border: 1px solid #ccc;
      background: #fff;
      overflow: hidden;
      input {
        border: 0;
        padding-left: 20px;
        font-size: 18px;
        height: 100%;
        outline: none;
        padding-right: 100px;
      }
      span {
        position: absolute;
        top: 50%;
        right: 20px;
        transform: translateY(-50%);
        color: rgba(0, 0, 0, 0.75);
        font-size: 18px;
      }
    }
    .saveBtn {
      button {
        width: 98px;
        height: 36px;
        &:nth-of-type(1) {
          margin-right: 16px;
          padding: 0 16px;
          font-size: 16px;
          color: #fc5531;
          border: 1px solid #fc5531;
          border-radius: 18px;
          white-space: nowrap;
          &:hover {
            background-color: #fff5f2;
          }
        }
        &:nth-of-type(2) {
          padding: 0 16px;
          font-size: 16px;
          color: #fff;
          border: none;
          border-radius: 18px;
          white-space: nowrap;
          background: #fc5531;
          &:hover {
            background: #fc1944;
          }
        }
      }
    }
    .userInfo {
      .pic-author {
        margin: 0 26px 0 14px;
        width: 32px;
        height: 32px;
        overflow: hidden;
        border-radius: 50%;
      }
    }
  }
}
</style>